package com.ternnetwork.core.accesscontroll.service.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;

import javax.persistence.criteria.Predicate;

import com.ternnetwork.commons.utils.ValidateUtils;
import com.ternnetwork.core.accesscontroll.dto.UserDto;
import com.ternnetwork.core.accesscontroll.dto.UserEditDto;
import com.ternnetwork.core.accesscontroll.dto.UserProfileDto;
import com.ternnetwork.core.accesscontroll.enums.CertificateType;
import com.ternnetwork.core.accesscontroll.vo.SimpleUser;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import com.ternnetwork.commons.model.http.BaseResponse;
import com.ternnetwork.commons.utils.DateUtils;
import com.ternnetwork.core.accesscontroll.dto.UserQueryDto;
import com.ternnetwork.core.accesscontroll.entity.Role;
import com.ternnetwork.core.accesscontroll.entity.User;
import com.ternnetwork.core.accesscontroll.entity.UserRole;
import com.ternnetwork.core.accesscontroll.entity.UserRoleId;
import com.ternnetwork.core.accesscontroll.enums.Gender;
import com.ternnetwork.core.accesscontroll.enums.messages.UserMessages;
import com.ternnetwork.core.accesscontroll.repository.DepartmentRepository;
import com.ternnetwork.core.accesscontroll.repository.RoleRepository;
import com.ternnetwork.core.accesscontroll.repository.UserRepository;
import com.ternnetwork.core.accesscontroll.repository.UserRoleRepository;
import com.ternnetwork.core.accesscontroll.service.UserService;

@Service
public class UserServiceImpl  implements UserService {
	
	
   @Autowired
   private UserRepository userRepository;

   @Autowired
   private DepartmentRepository departmentRepository;

   @Autowired
   private RoleRepository roleRepository;

   @Autowired
   private UserRoleRepository userRoleRepository;
	
   private static final String DELETED_USER_NAME_FLAG="_deleted_";	

   
   public static PasswordEncoder passwordEncoder=new BCryptPasswordEncoder();
   
   @Override
   public BaseResponse getOne(Long id) {
		
		BaseResponse  baseResponse=new BaseResponse();
		
		User t=userRepository.getOne(id);
		
		baseResponse.setSccucess(t);
		
		return baseResponse;
	}
	
   @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor=Exception.class)
    public BaseResponse add(UserDto dto) throws Exception {

   	   BaseResponse  baseResponse=checkProperties(dto);

	   if(baseResponse.getStatus().intValue()==0) {
		   return baseResponse;
	   }

   	    User t=new User();

	    BeanUtils.copyProperties(dto, t);

	    setOtherAttribute(dto, t);

    	long totalCount=userRepository.getCountByName(t.getName()); 
    	
      	if(totalCount>0L) {
    		baseResponse.setFailed(UserMessages.FAILED_MESSAGE_NAME_ZH_CAN_NOT_BE_DUPLICATE.getName());
    		return baseResponse;
    	}
      	
        t=setDepartment(t);
      
        String encodedPassword= passwordEncoder.encode(t.getPassword());
    
        t.setPassword(encodedPassword.trim());
    	
    	if(t.getGender()==null) {
 		   t.setGender(Gender.UNKNOWN);
 		}
    	t.setAccountNonExpired(true);
		t.setAccountNonLocked(true);
		t.setEnable(true);
		t.setCredentialsNonExpired(true);
		t.setCreateTime(new Date());
		t.setUpdateTime(t.getCreateTime());

		userRepository.saveAndFlush(t);
       	
       	addRoleUser(t,t.getRoleIds());
       	
		baseResponse.setSccucess(t);
		
		return baseResponse;
	}
    
    
    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor=Exception.class)
    public BaseResponse update(UserEditDto dto)  throws Exception{

		BaseResponse  baseResponse=checkPropertiesForUpdate(dto);
		//校验不通过
		if(baseResponse.getStatus().intValue()==0) {
			return baseResponse;
		}

		User t=new User();

		BeanUtils.copyProperties(dto, t);

		setOtherAttribute(dto, t);

    	t=setDepartment(t);
    	
       	User old=userRepository.getOne(t.getId());
    	
    	if(old==null||old.getId().longValue()==0L) {
    		baseResponse.setFailedForObjectNotExist();
    		return baseResponse;
    	}
    	
       	if(!StringUtils.isEmpty(t.getPassword())) {
       	  String encodedPassword= passwordEncoder.encode(t.getPassword());
          old.setPassword(encodedPassword.trim());
    	}
        
       	old.setRealName(t.getRealName());
       	old.setEmail(t.getEmail());
       	old.setMobilePhone(t.getMobilePhone());
       	old.setGender(t.getGender());
       	old.setAccountNonLocked(t.getAccountNonLocked());
       	old.setAccountNonExpired(t.getAccountNonExpired());
       	old.setDepartment(t.getDepartment());
        old.setCertificateNo(t.getCertificateNo());
        old.setCertificateType(t.getCertificateType());
		old.setPhone(t.getPhone());
    	updateRoleUser(old,t.getRoleIds());
       	userRepository.saveAndFlush(old);
   		baseResponse.setSccucess();
		return baseResponse;
	}

	private void setOtherAttribute(UserDto dto, User t) {
		if(!StringUtils.isEmpty(dto.getGender())){
			t.setGender(Gender.valueOf(dto.getGender()));
		}
		if(!StringUtils.isEmpty(dto.getCertificateType())){
			t.setCertificateType(CertificateType.valueOf(dto.getCertificateType()));
		}
	}

	@Override
    public BaseResponse findByPage(Pageable pageable,UserQueryDto dto) {
    	  
    	 BaseResponse  baseResponse=new BaseResponse();
    	
     	 Page<User> page =userRepository.findAll((root, criteriaQuery, criteriaBuilder) -> {

         List<Predicate> predicates = new ArrayList<>();

         if(!StringUtils.isEmpty(dto.getName())){
             predicates.add(criteriaBuilder.like(root.get("name").as(String.class),"%".concat(dto.getName().toLowerCase()).concat("%")));
         }
         
         if(!StringUtils.isEmpty(dto.getRealName())){
             predicates.add(criteriaBuilder.like(root.get("realName").as(String.class),"%".concat(dto.getRealName().toLowerCase()).concat("%")));
         }
         
         if(!StringUtils.isEmpty(dto.getEmail())){
             predicates.add(criteriaBuilder.equal(root.get("email").as(String.class), dto.getEmail()));
         }
         
         if(!StringUtils.isEmpty(dto.getDepartmentName())){
             predicates.add(criteriaBuilder.like(root.get("department").get("nameZh").as(String.class),"%".concat(dto.getDepartmentName().toLowerCase()).concat("%")));
         }
         
         if(!StringUtils.isEmpty(dto.getPhone())){
             predicates.add(criteriaBuilder.equal(root.get("phone").as(String.class), dto.getPhone()));
         }

		if(!StringUtils.isEmpty(dto.getCertificateNo())){
				 predicates.add(criteriaBuilder.equal(root.get("certificateNo").as(String.class), dto.getCertificateNo()));
		}

		 if(!StringUtils.isEmpty(dto.getCertificateType())){
				 predicates.add(criteriaBuilder.equal(root.get("certificateType").as(CertificateType.class), CertificateType.valueOf(dto.getCertificateType())));
		 }
         
         predicates.add(criteriaBuilder.equal(root.get("deleted").as(Boolean.class),false));

         Predicate[] predicateArray = new Predicate[predicates.size()];

         return criteriaBuilder.and(predicates.toArray(predicateArray));
     }, pageable);
     	 
     	baseResponse.setSccucess(page);
    	
    	return baseResponse;
    }
    
	
    @Override
	public BaseResponse deleteById(Long id){
		
		BaseResponse res=new BaseResponse();

		User t=userRepository.getOne(id);

		if(t==null||t.getId().longValue()==0L){

			res.setFailedForObjectNotExist();

			return res;
		}

		if(t.getId()==1L){
			res.setFailed(UserMessages.FAILED_MESSAGE_OBJECT_CAN_NOT_BE_DELETED.getName());
			return res;
		}

		t.setDeleted(true);

		t.setDeletedTime(new Date());

		t.setName(t.getName().concat(DELETED_USER_NAME_FLAG).concat(DateUtils.format(new Date(), DateUtils.FORMAT_DATE_TIME_YYYYMMDDHHMMSS)));

		if(!StringUtils.isEmpty(t.getWechatId())){

			t.setWechatId(t.getWechatId().concat(DELETED_USER_NAME_FLAG).concat(DateUtils.format(new Date(), DateUtils.FORMAT_DATE_TIME_YYYYMMDDHHMMSS)));
		}
		userRepository.saveAndFlush(t);

		res.setSccucess();

		return res;
	}
    

	
	 /**
	    * 检查属性
	    * @param t
	    * @return
	    */
	   private BaseResponse checkProperties (UserDto t) {
	  	 
			BaseResponse  baseResponse=new BaseResponse();
			    
		    if(StringUtils.isEmpty(t.getName())) {
		    	baseResponse.setFailed(UserMessages.FAILED_MESSAGE_NAME_ZH_CAN_NOT_BE_EMPTY.getName());
		    	return baseResponse;
		    }
		    
		    if(StringUtils.isEmpty(t.getPassword())) {
		    	baseResponse.setFailed(UserMessages.FAILED_MESSAGE_PASSWORD_CAN_NOT_BE_EMPTY.getName());
		    	return baseResponse;
		    }

		   if(!t.getPassword().equals(t.getConfirmPassword())) {
			   baseResponse.setFailed(UserMessages.FAILED_MESSAGE_PASSWORD_IS_NOT_EQ_CONFIRM_PASSWORD.getName());
			   return baseResponse;
		   }
		
		    if(StringUtils.isEmpty(t.getEmail())) {
		    	baseResponse.setFailed(UserMessages.FAILED_MESSAGE_EMAIL_CAN_NOT_BE_EMPTY.getName());
		    	return baseResponse;
		    }
		    baseResponse.setSccucess();
		    return baseResponse;
	   }
	   
	   
	   private BaseResponse checkPropertiesForUpdate (UserDto t) {
		  	 
			BaseResponse  baseResponse=new BaseResponse();
			    
		    if(StringUtils.isEmpty(t.getEmail())) {
		    	baseResponse.setFailed(UserMessages.FAILED_MESSAGE_EMAIL_CAN_NOT_BE_EMPTY.getName());
		    	return baseResponse;
		    }

		   if(!StringUtils.isEmpty(t.getPassword())&&!t.getPassword().equals(t.getConfirmPassword())) {
			   baseResponse.setFailed(UserMessages.FAILED_MESSAGE_PASSWORD_IS_NOT_EQ_CONFIRM_PASSWORD.getName());
			   return baseResponse;
		   }
		    baseResponse.setSccucess();
		    return baseResponse;
	   }
	   
	   
	   
	   private User setDepartment(User t) {
		   
	     	if(!StringUtils.isEmpty(t.getDepartmentId())){
	      		long departmentId=Long.parseLong(t.getDepartmentId());
	      		long departmentTotalCount=departmentRepository.getCountById(departmentId);
	          	if(departmentTotalCount>0L) {
	          		t.setDepartment(departmentRepository.getOne(departmentId));
	          	}
	       	}
	     	return t;
	   }
	   
	   
	   /**
	    * 添加用户与角色的关联
	    * @param t
	    * @param roleIds
	    */
       private void addRoleUser(User t,String roleIds) {
		   
		   if(!StringUtils.isEmpty(roleIds)) {
			   
			   String[] roleIdsArray=roleIds.split(",");
			
			   for(String roleIdStr:roleIdsArray) {
				     Long roleId=Long.parseLong(roleIdStr);
				     if(!checkIsRoleIdExist(t.getUserRoles(), roleId)) {
				    	 Role role=roleRepository.getOne(roleId);
					     UserRoleId userRoleId=new UserRoleId();
						 userRoleId.setRoleId(role.getId());
						 userRoleId.setUserId(t.getId());
						 UserRole userRole=new UserRole();
						 userRole.setId(userRoleId);
						 userRole.setUser(t);
						 userRole.setRole(role);
						 t.getUserRoles().add(userRole);
				     }
			   }
		   }
		
	   }
       
  
	   /**
	    * 更新用户与角色的关联
	    * @param t
	    * @param roleIds
	    */
	   private void updateRoleUser(User t,String roleIds) {
		    List<Long> roleIdList=covertToRoleIdList(roleIds);
		    userRoleRepository.deleteAllByUserIdAnRoleIdList(t.getId(), roleIdList);
		    addRoleUser(t, roleIds);
	   }
	   
	   /**
	    * 
	    * @param roleIds
	    * @return
	    */
	   private List<Long> covertToRoleIdList(String roleIds) {
		   List<Long> roleIdList=new ArrayList<Long>(10);
		   if(!StringUtils.isEmpty(roleIds)) {
		   String[] roleIdsArray=roleIds.split(",");
		     for(String roleIdStr:roleIdsArray) {
			     Long roleId=Long.parseLong(roleIdStr);
			     roleIdList.add(roleId);
		     }
		   }
		   return roleIdList;
	   }
	   
	   /**
	    * 检是角色ID否存在
	    * @param userRoles
	    * @param roleId
	    * @return
	    */
	   private Boolean checkIsRoleIdExist(Set<UserRole> userRoles,Long roleId) {
    		 for(UserRole ur:userRoles) {
				 if(ur.getRole().getId().longValue()==roleId.longValue()) {
					 return true;
				 }
			 }
    		 return false;
       }
	   @Override
	   public User findByName(String name) {
		  List<User> userList=userRepository.findAllByName(name);
		  return userList.size()>0?userList.get(0):null;
	   }


	  public List<User> findByRoleName(String nameEn) {
		return userRepository.findAllByRoleName(nameEn);
	  }

	@Override
	public BaseResponse updatePassword(String orinPassword, String newPassword, String confirmNewPassword,String userName) throws Exception {

		BaseResponse baseResponse=new BaseResponse();

		User user=findByName(userName);

		String oldEncodePassword=user.getPassword();

		if(StringUtils.isEmpty(orinPassword)){

			baseResponse.setFailed(UserMessages.FAILED_MESSAGE_PASSWORD_CAN_NOT_BE_EMPTY.getName());

			return baseResponse;
		}

		if(!passwordEncoder.matches(orinPassword,oldEncodePassword)){

			baseResponse.setFailed(UserMessages.FAILED_MESSAGE_OLD_PASSWORD_IS_NOT_CORRECT.getName());

			return baseResponse;
		}

		if(StringUtils.isEmpty(newPassword)){

			baseResponse.setFailed(UserMessages.FAILED_MESSAGE_PASSWORD_CAN_NOT_BE_EMPTY.getName());

			return baseResponse;
		}

		if(!newPassword.equals(confirmNewPassword)){

			baseResponse.setFailed(UserMessages.FAILED_MESSAGE_NEW_PASSWORD_IS_NOT_EQ_CONFIRM_NEW_PASSWORD.getName());

			return baseResponse;
		}

		if(passwordEncoder.matches(newPassword,oldEncodePassword)){

			baseResponse.setFailed(UserMessages.FAILED_MESSAGE_NEW_PASSWORD_IS_EQ_OLD_PASSWORD.getName());

			return baseResponse;
		}

		String encodedNewPassword= passwordEncoder.encode(newPassword);

		user.setPassword(encodedNewPassword);

		userRepository.saveAndFlush(user);

		baseResponse.setSccucess();

	   	return baseResponse;
	}


	@Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor=Exception.class)
	public BaseResponse updateProfile(UserProfileDto dto, String userName){

		BaseResponse  baseResponse=new BaseResponse();

		User old=findByName(userName);

		if(StringUtils.isEmpty(dto.getRealName())) {
			baseResponse.setFailed(UserMessages.FAILED_MESSAGE_NAME_ZH_CAN_NOT_BE_EMPTY.getName());
			return baseResponse;
		}

		if(!StringUtils.isEmpty(dto.getEmail())) {
			if(!ValidateUtils.isEmail(dto.getEmail())){
				baseResponse.setFailed(UserMessages.FAILED_MESSAGE_EMAIL_WRONG_FORMAT.getName());
				return baseResponse;
			}
		}

		BeanUtils.copyProperties(dto,old);

		userRepository.saveAndFlush(old);

		baseResponse.setSccucess();

		return baseResponse;
	}

	public BaseResponse findAllExceptName(String name){

		List<User> userList=userRepository.findAllExceptName(name);

		List<SimpleUser> simpleUserList=new ArrayList<>();

		for(User user:userList){

			SimpleUser simpleUser=new SimpleUser();

			BeanUtils.copyProperties(user,simpleUser);

			simpleUserList.add(simpleUser);
		}

		BaseResponse baseResponse=new BaseResponse();
		baseResponse.setSccucess(simpleUserList);
		return baseResponse;
	}


}
